package com.xxl.job.executor.service.jobhandler;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.biyuhan.minioFile.service.MinioService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.executor.entity.CwDealPerDayDto;
import com.xxl.job.executor.entity.ZbfExportTaskDetail;
import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
import org.apache.http.entity.ContentType;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.engine.Engine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Array;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * XxlJob开发示例（Bean模式）
 *
 * 开发步骤：
 *      1、任务开发：在Spring Bean实例中，开发Job方法；
 *      2、注解配置：为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")"，注解value值对应的是调度中心新建任务的JobHandler属性的值。
 *      3、执行日志：需要通过 "XxlJobHelper.log" 打印执行日志；
 *      4、任务结果：默认任务结果为 "成功" 状态，不需要主动设置；如有诉求，比如设置任务结果为失败，可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果；
 *
 * @author xuxueli 2019-12-11 21:52:51
 */
@Component
public class SampleXxlJob {
    private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);

    private String url="http://localhost:1999";
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private MinioService minioService;



    @XxlJob(value = "test01")
    public void test01(){
        JSONObject jsonObject = JSON.parseObject(XxlJobHelper.getJobParam());
        System.out.println("执行任务---------------");
        //请求参数
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            //调取接口，获取头表数据
            ResponseEntity<Map> mapResponseEntity = restTemplate.getForEntity(url+"/header/list",Map.class,map);
            Map body = mapResponseEntity.getBody();
            System.out.println(JSON.toJSONString(body.get("rows")));
        }catch (Exception e){
            System.out.println(e);
        }


    }

    /**
     * 2、分片广播任务
     */
    @XxlJob("shardingJobHandler")
    public void shardingJobHandler() throws Exception {
        // 分片参数 : 当前分片序号
        int shardIndex = XxlJobHelper.getShardIndex();
        // 分片参数 : 总分片数
        int shardTotal = XxlJobHelper.getShardTotal();
        //创建查询条件
        Map<String , Object> queryMap = new HashMap<>();
        queryMap.put("taskStatus",0);//任务状态

        try {
            //扫描任务头表
            Map body = restTemplate.postForEntity(url+"/header/list", queryMap , Map.class).getBody();

            //判断有多少条任务
            if (Integer.valueOf(body.get("total").toString()) == 0){
                XxlJobHelper.log("未发现任务");
            }else {
                //获取准备状态下的任务
                Map<String, Object> updateMap = new HashMap<>();
                List<Map<String,Object>> list = (ArrayList<Map<String, Object>>) body.get("rows");
                for (Map<String, Object> map : list) {
                    updateMap.put("id", map.get("id"));
                    updateMap.put("taskStatus", 1l);
                    //修改任务状态
                    restTemplate.put(url + "/header/edit", updateMap);
                }
            }

            //获取状态为1的数据信息
            queryMap.put("taskStatus",1l);
            body = restTemplate.postForEntity(url+"/header/list", queryMap , Map.class).getBody();

            if ((Integer) body.get("total") == 0){
                XxlJobHelper.log("未发现任务");
            }else {

                List<Map<String,Object>> list = (ArrayList<Map<String, Object>>) body.get("rows");
                Map<String , Object> map = new HashMap<>();
                for (Map<String, Object> dataMap : list) {

                    XxlJobHelper.log("任务编号：{},共{}个分片",dataMap.get("id"),dataMap.get("taskTailNum"));
                    System.out.println("任务编号：{"+dataMap.get("id")+"},共需{"+dataMap.get("taskTailNum")+"}个分片");

                    //获取任务明细数量
                    Integer taskTailNum = (Integer) dataMap.get("taskTailNum");

                    // 业务逻辑
                    for (int i = 0; i < taskTailNum; i++) {
                        if (i == shardIndex) {
                            XxlJobHelper.log("第 {} 片, 命中分片开始处理", i);
                            //添加查询条件
                            JSONObject taskSql = JSON.parseObject(dataMap.get("taskSql").toString());
                            String operationType = taskSql.get("operationType").toString();
                            String startDate = taskSql.get("startDate").toString();
                            String endDate = taskSql.get("endDate").toString();
                            //判断是收入还是支出 taskSql -> {"startDate":"2021-08-01","endDate":"2021-08-05","operationType":1}

                            map.put("pageNum",i);//当前页
                            map.put("pageSize",100);//每页条数
                            map.put("operationType",operationType);//支出、收入
                            map.put("startDate",startDate);//开始时间
                            map.put("endDate",endDate);//结束时间
                            //生成excel表格
                            getExcel(map , shardIndex);

                            //修改状态
                            overUpdate((Long) dataMap.get("id")  ,shardIndex);

                        }else {
                            XxlJobHelper.log("第 {} 片, 忽略", i);
                        }
                    }

                }
            }

        }catch (Exception e){
            System.out.println(e);
        }


    }

    /**
     * 假定为最后一个执行完成的，修改任务状态，压缩文件，上传文件
     * @param id
     * @param shardIndex
     */
    public void overUpdate(Long id , int shardIndex){
        Map<String,Object> queryMap = new HashMap<>();
        //1、查询版本号   2、修改完成数量
        queryMap.put("id" , id);
        //查询任务信息
        Map body = restTemplate.getForEntity(url + "/header/{id}", Map.class, queryMap).getBody();
        Map<String , Object> versionMap = (Map<String, Object>) body.get("data");

        //修改明细数量
        Map<String , Object> updateMap = new HashMap<>();
        updateMap.put("id" , versionMap.get("id"));
        updateMap.put("version" , versionMap.get("version"));
        restTemplate.put(url+"//header/editVersion" , updateMap);
        System.out.println("修改成功！");
        //查询任务信息
        body = restTemplate.getForEntity(url + "/header/{id}", Map.class, queryMap).getBody();
        versionMap = (Map<String, Object>) body.get("data");

        try {
            //类型改为Integer试试
            Integer taskTailNum = Integer.valueOf(versionMap.get("taskTailNum").toString());//总数
            Integer taskTailOkNum = (Integer) versionMap.get("taskTailOkNum");  //明细数量

            if (taskTailNum <= taskTailOkNum){
                System.out.println("批量导出完成！");

                JSONObject taskSql = JSON.parseObject(versionMap.get("taskSql").toString());
                String operationType = taskSql.get("operationType").toString();
                //判断是收入还是支出 taskSql -> {"startDate":"2021-08-01","endDate":"2021-08-05","operationType":1}

                if (operationType.equals("1")){
                    operationType = "收入";
                }else {
                    operationType = "支出";
                }
                String startDate = taskSql.get("startDate").toString();
                String endDate = taskSql.get("endDate").toString();
                //文件名称
                List<File> fileNames = new ArrayList<>();
                for (int i = 0; i < taskTailNum; i++) {
                    fileNames.add(new File("E:/excel/"+startDate+"-"+endDate+"("+operationType+i+")"+ ".xlsx"));
                }
                File[] files = new File[fileNames.size()];
                for (int i = 0; i < fileNames.size(); i++) {
                    files[i] = fileNames.get(i);
                }
                //压缩excel文档到压缩包
                String zipPath = "E:/excel/zip/"+startDate+"-"+endDate+"("+operationType+")"+ ".zip";
                File zipFile = new File(zipPath);
                zipFiles(files , zipFile);

                //上传压缩包到minio
                String minioPath = "测试路径";
                FileInputStream fileInputStream = new FileInputStream(zipFile);
                MultipartFile multipartFile = new MockMultipartFile(zipFile.getName(), zipFile.getName(),
                        ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
                //将zip保存在minio中
                String s = minioService.updateFile(multipartFile);
                System.err.println(s);

                //删除临时文件
                for (File file:fileNames) {
                    if (!file.exists()) {
                        System.out.println("删除文件失败!文件不存在");
                    } else {
                        file.delete();
                    }
                }
                if (zipFile.exists()){
                    zipFile.delete();
                }

                //添加任务明细数据
                ZbfExportTaskDetail detail = new ZbfExportTaskDetail();
                detail.setId(new Random().nextLong()+1); //id
                detail.setFileName(startDate+"-"+endDate+"("+operationType+")"+ ".zip"); //文件名称
                detail.setFilePath(s); //保存路径
                detail.setTaskHeaderId((Long) versionMap.get("id")); //头表id
                detail.setShardId((long) shardIndex); //分片id
                restTemplate.postForEntity(url+"/detail/add" , detail , Map.class);

                updateMap.remove("version");
                //修改任务状态
                updateMap.put("taskStatus" , 2);
                restTemplate.put(url+"/header/edit" , updateMap);
            }
        }catch (Exception e){
            System.out.println(e);
        }
        System.out.println("文件上传成功！");
    }

    /**
     * 多文件压缩
     * @param srcFiles
     * @param zipFile
     */
    public static void zipFiles(File[] srcFiles, File zipFile) {
        // 判断压缩后的文件存在不，不存在则创建
        if (!zipFile.exists()) {
            try {
                zipFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 创建 FileOutputStream 对象
        FileOutputStream fileOutputStream = null;
        // 创建 ZipOutputStream
        ZipOutputStream zipOutputStream = null;
        // 创建 FileInputStream 对象
        FileInputStream fileInputStream = null;

        try {
            // 实例化 FileOutputStream 对象
            fileOutputStream = new FileOutputStream(zipFile);
            // 实例化 ZipOutputStream 对象
            zipOutputStream = new ZipOutputStream(fileOutputStream);
            // 创建 ZipEntry 对象
            ZipEntry zipEntry = null;
            // 遍历源文件数组
            for (int i = 0; i < srcFiles.length; i++) {
                // 将源文件数组中的当前文件读入 FileInputStream 流中
                fileInputStream = new FileInputStream(srcFiles[i]);
                // 实例化 ZipEntry 对象，源文件数组中的当前文件
                zipEntry = new ZipEntry(srcFiles[i].getName());
                zipOutputStream.putNextEntry(zipEntry);
                // 该变量记录每次真正读的字节个数
                int len;
                // 定义每次读取的字节数组
                byte[] buffer = new byte[1024];
                while ((len = fileInputStream.read(buffer)) > 0) {
                    zipOutputStream.write(buffer, 0, len);
                }
            }
            zipOutputStream.closeEntry();
            zipOutputStream.close();
            fileInputStream.close();
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
      }


    /**
     *  数据导出excel
     * @param map 导出数据查询条件
     * @param shardIndex 分片id
     */
    @XxlJob("getExcel")
    public void getExcel(Map<String , Object> map , Integer shardIndex){
        XxlJobHelper.log("执行数据导出到excel");
        try {
            if (map == null){
                map = new HashMap<>();
            }
            Map body = restTemplate.postForEntity(url+"/jinrong/list" , map , Map.class).getBody();
//        Map body = restTemplate.getForEntity(url+"/jinrong/list", Map.class, map).getBody();
            //获取导出数据
            String s = JSON.toJSONString(body.get("data"));

            List<CwDealPerDayDto> list = JSONObject.parseArray(s, CwDealPerDayDto.class);

            //开始时间
            String startDate = (String) body.get("startDate");
            //结束时间
            String endDate = (String) body.get("endDate");
            //文件名称
            String fileName;
            if (map.get("operationType").toString().equals("1")){
               fileName =  "E:/excel/"+startDate+"-"+endDate+"(收入"+shardIndex+")"+ ".xlsx";
            }else {
                fileName =  "E:/excel/"+startDate+"-"+endDate+"(支出"+shardIndex+")"+ ".xlsx";
            }
            // 根据用户传入字段 设置忽略字段
            Set<String> excludeColumnFiledNames = new HashSet<String>();
            excludeColumnFiledNames.add("businessId");
            excludeColumnFiledNames.add("businessName");
            excludeColumnFiledNames.add("operationType");

            // 这里 需要指定写用哪个class去写，然后写到第一个sheet，名字为模板 然后文件流会自动关闭
            EasyExcel.write(fileName, CwDealPerDayDto.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("营业报表")
                    .doWrite(list);
        }catch (Exception e){
            System.out.println(e);
        }

        System.out.println("数据导出成功！");
    }




        /**
         * 删除es上三十天之前的log日志
         * @throws IOException
         */
    @XxlJob(value = "delEslog")
    public void delEslog() throws IOException {
        System.out.println("执行删除日志任务");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String maxDateStr = sdf.format(new Date());
        String minDateStr = "";
        Calendar calc = Calendar.getInstance();
        try {
            calc.setTime(sdf.parse(maxDateStr));
            calc.add(Calendar.DATE, -30);
            Date minDate = calc.getTime();
            minDateStr = sdf.format(minDate);
            System.out.println("当前时间:" + maxDateStr);
            System.out.println("30天之前:" + minDateStr);

            TestJobapp testJobapp = new TestJobapp();


            //判断索引是否存在
            GetIndexRequest exist = new GetIndexRequest(minDateStr);
            boolean exists = restHighLevelClient.indices().exists(exist, RequestOptions.DEFAULT);
            if (exists) {
                System.err.println("删除日志");
                //根据索引删除日志
                DeleteIndexRequest request = new DeleteIndexRequest(minDateStr);
                //设置超时
                request.timeout(TimeValue.timeValueMinutes(2));
                AcknowledgedResponse delete = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
                //返回值是true表示删除成功
                System.err.println(delete.isAcknowledged());
            }

        } catch (ParseException e1) {
            e1.printStackTrace();
        }

    }

    /**
     * 导出excel前端直接下载
     * @param response
     * @param fileName
     * @param excel
     */
    public void setRes(HttpServletResponse response , String fileName , HSSFWorkbook excel){

        try {

            response.setHeader("Content-disposition", "attachment;filename=" + fileName);
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            OutputStream out = response.getOutputStream();
            excel.write(out);
            out.flush();
            out.close();

        } catch (IOException e) {
            e.printStackTrace();

        }
    }
}
